package com.ruoyi.web.service.impl;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.RoundingMode;
import java.util.List;

import cn.hutool.core.util.NumberUtil;
import cn.hutool.crypto.symmetric.AES;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.web.constant.WalletConstant;
import com.ruoyi.web.domain.Currency;
import com.ruoyi.web.domain.RechargeRecord;
import com.ruoyi.web.domain.UserAddress;
import com.ruoyi.web.enums.ImputationRecordEnum;
import com.ruoyi.web.enums.UserEnums;
import com.ruoyi.web.mapper.UserAddressMapper;
import com.ruoyi.web.runner.SendServiceCharge;
import com.ruoyi.web.service.ICurrencyService;
import com.ruoyi.web.utils.EthUtil;
import com.ruoyi.web.utils.IdWorker;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import com.ruoyi.web.mapper.ImputationRecordMapper;
import com.ruoyi.web.domain.ImputationRecord;
import com.ruoyi.web.service.IImputationRecordService;
import org.tron.trident.core.ApiWrapper;
import org.tron.trident.core.contract.Trc20Contract;
import org.web3j.protocol.Web3j;
import org.web3j.protocol.core.methods.response.EthSendTransaction;
import org.web3j.protocol.http.HttpService;
import org.web3j.utils.Convert;

/**
 * 归集记录Service业务层处理
 *
 * @author ruoyi
 * @date 2023-01-06
 */
@Service
@AllArgsConstructor
@Slf4j
public class ImputationRecordServiceImpl implements IImputationRecordService
{
    private final ImputationRecordMapper imputationRecordMapper;
    private final UserAddressMapper userAddressMapper;
    private final ICurrencyService currencyService;
    private final SendServiceCharge serviceCharge;
    private final IdWorker idWorker;

    @Async
    public R<String> create(ImputationRecord imputationRecord) {
        try {
            // 获取地址列表
            List<UserAddress> accountList = userAddressMapper.selectUserAddressList(new UserAddress(){{
                setChain(imputationRecord.getChain());
            }});
            if (!imputationRecord.getCurrency().equals(WalletConstant.USDT)) {
              return R.fail("Only USDT collection is supported");
            }
            // 获取币种
            Currency currency = currencyService.getCurrency(imputationRecord.getChain());
            // 遍历地址
            for (UserAddress address : accountList) {
                // 保存归集列表
                ImputationRecord imputationRecordSave = new ImputationRecord();
                imputationRecordSave.setId(idWorker.nextId());
                // 到账地址
                imputationRecordSave.setArrivalAddress(imputationRecord.getArrivalAddress());
                // 币种
                imputationRecordSave.setCurrency(imputationRecord.getCurrency());
                // 公链
                imputationRecordSave.setChain(imputationRecord.getChain());
                // 来源地址
                imputationRecordSave.setSourceAddress(address.getAddress());
                // 用户ID
                imputationRecordSave.setUserId(address.getUserId());
                try {
                    // bsc / eth 链, 走web3j
                    if (address.getChain().equals(UserEnums.Chain.ETH.getValue()) ||
                            address.getChain().equals(UserEnums.Chain.BSC.getValue())
                    ) {
                        EthUtil ethUtil = new EthUtil(Web3j.build(new HttpService(currency.getNodeAddress() + currency.getApiKey())));
                        // 获取用户余额
                        String balance = ethUtil.getEthTrc20Balance(address.getAddress(), currency.getSmartContractAddress());
                        // 保留币种列表中该币种指定的精度，转化成String类型数据
                        BigDecimal bigDecimal = Convert.fromWei(new BigDecimal(balance), Convert.Unit.ETHER)
                                .setScale(Math.toIntExact(currency.getAccuracy()), RoundingMode.DOWN);
                        imputationRecordSave.setQuantity(bigDecimal);
                        // 比较USC钱包余额和归集的数量
                        boolean greaterOrEqual = NumberUtil.isGreaterOrEqual(bigDecimal, imputationRecord.getQuantity());
                        // 如果满足归集条件
                        if (greaterOrEqual) {
                            // 手续费
                            serviceCharge.ethFee(new RechargeRecord(){{
                                setArrivalAddress(address.getAddress());
                            }},imputationRecord.getChain());
                            // 私钥解密
                            AES aes = new AES(WalletConstant.AES_KEY);
                            String decryptStr = aes.decryptStr(address.getPrivateKey());
                            // 交易
                            EthSendTransaction ethSendTransaction = ethUtil.ethSend(
                                    decryptStr,
                                    address.getAddress(),
                                    imputationRecord.getArrivalAddress(),
                                    currency.getSmartContractAddress(),
                                    balance
                            );
                            if (ethSendTransaction.getError() == null) {
                                // 状态（1：归集中，2：成功，3：失败）
                                imputationRecordSave.setState(ImputationRecordEnum.Status.SUCCESS.getValue());
                                // 交 易 哈 希
                                String transactionHash = ethSendTransaction.getTransactionHash();
                                imputationRecordSave.setTransactionHash(transactionHash);
                                imputationRecordMapper.insertImputationRecord(imputationRecordSave);
                            } else {
                                throw new RuntimeException();
                            }
                        }
                    }
                    // 波场链
                    if (address.getChain().equals(UserEnums.Chain.TRX.getValue())) {
                        // 解密私钥
                        AES aes = new AES(WalletConstant.AES_KEY);
                        String decryptStr = aes.decryptStr(address.getPrivateKey());
                        // Nile 测试网
//                        ApiWrapper wrapper = ApiWrapper.ofNile(decryptStr);
                        // main 主网
                        ApiWrapper wrapper = ApiWrapper.ofMainnet(decryptStr,currency.getApiKey());
                        org.tron.trident.core.contract.Contract contract = wrapper.getContract(currency.getSmartContractAddress());
                        // TRON余额查询
                        Trc20Contract trc20Contract = new Trc20Contract(contract, address.getAddress(), wrapper);
                        BigInteger bigInteger = trc20Contract.balanceOf(address.getAddress());
                        String balance = NumberUtil.toBigDecimal(String.valueOf(bigInteger))
                                .movePointLeft(6)
                                .stripTrailingZeros()
                                .toPlainString();
                        BigDecimal bigDecimal = NumberUtil.toBigDecimal(balance);
                        // 比较是否满足归集条件
                        boolean greaterOrEqual = NumberUtil.isGreaterOrEqual(bigDecimal, NumberUtil.toBigDecimal(imputationRecord.getQuantity()));
                        if (greaterOrEqual) {
                            // 手续费
                            serviceCharge.tronFee(new RechargeRecord(){{
                                setArrivalAddress(address.getAddress());
                            }});
                            // 转账
                            String transfer = null;
                            try {
                                transfer = trc20Contract.transfer(
                                        imputationRecord.getArrivalAddress(),
                                        bigInteger.longValue(),
                                        0,
                                        "memo",
                                        100000000);
                            } catch (RuntimeException e) {
                                throw new Exception();
                            }
                            // 归集成功
                            // 状态（1：归集中，2：成功，3：失败）
                            imputationRecordSave.setState(ImputationRecordEnum.Status.SUCCESS.getValue());
                            // 交 易 哈 希
                            imputationRecordSave.setTransactionHash(transfer);
                            imputationRecord.setQuantity(bigDecimal);
                            imputationRecordMapper.insertImputationRecord(imputationRecordSave);
                        }
                    }
                } catch (Exception e) {
                    // 状态（1：归集中，2：成功，3：失败）
                    imputationRecordSave.setState(ImputationRecordEnum.Status.FAIL.getValue());
                    imputationRecordMapper.insertImputationRecord(imputationRecordSave);
                    e.printStackTrace();
                }
            }
            return R.ok();
        } catch (Exception e) {
            log.error("归集失败");
            return R.fail();
        }
    }



    /**
     * 查询归集记录
     *
     * @param id 归集记录主键
     * @return 归集记录
     */
    @Override
    public ImputationRecord selectImputationRecordById(Long id)
    {
        return imputationRecordMapper.selectImputationRecordById(id);
    }

    /**
     * 查询归集记录列表
     *
     * @param imputationRecord 归集记录
     * @return 归集记录
     */
    @Override
    public List<ImputationRecord> selectImputationRecordList(ImputationRecord imputationRecord)
    {
        return imputationRecordMapper.selectImputationRecordList(imputationRecord);
    }

    /**
     * 新增归集记录
     *
     * @param imputationRecord 归集记录
     * @return 结果
     */
    @Override
    public int insertImputationRecord(ImputationRecord imputationRecord)
    {
        imputationRecord.setCreateTime(DateUtils.getNowDate());
        return imputationRecordMapper.insertImputationRecord(imputationRecord);
    }

    /**
     * 修改归集记录
     *
     * @param imputationRecord 归集记录
     * @return 结果
     */
    @Override
    public int updateImputationRecord(ImputationRecord imputationRecord)
    {
        return imputationRecordMapper.updateImputationRecord(imputationRecord);
    }

    /**
     * 批量删除归集记录
     *
     * @param ids 需要删除的归集记录主键
     * @return 结果
     */
    @Override
    public int deleteImputationRecordByIds(Long[] ids)
    {
        return imputationRecordMapper.deleteImputationRecordByIds(ids);
    }

    /**
     * 删除归集记录信息
     *
     * @param id 归集记录主键
     * @return 结果
     */
    @Override
    public int deleteImputationRecordById(Long id)
    {
        return imputationRecordMapper.deleteImputationRecordById(id);
    }
}
